Skip to main content

Paginated / Lagged Queries


呈现分页数据是一种非常常见的UI模式,在TanStack Query中,它通过在查询键中包含页面信息来自动处理:

const result = useQuery({
queryKey: ['projects', page],
queryFn: fetchProjects
})

然而,如果运行这个简单的示例,您可能会注意到一些奇怪的现象:

UI在成功和加载状态之间跳来跳去,因为每个新页面都被视为全新的查询。

这种体验并不理想,不幸的是,这也是当今许多工具坚持的工作方式。但是,TanStack Query不同!正如您可能已经猜到的那样,TanStack Query具有一个很棒的功能,称为keepPreviousData,它允许我们解决这个问题。

使用keepPreviousData优化分页查询

考虑下面的例子,我们希望为查询增加一个pageIndex(或游标)。如果我们使用useQuery,从技术上讲,它仍然可以正常工作,但是UI会在每个页面或游标创建和销毁时在成功和加载状态之间跳来跳去。通过将keepPreviousData设置为true,我们可以获得以下几个新功能:

  • 在请求新数据时,仍然可以使用上一次成功获取的数据,即使查询键已更改。
  • 当新数据到达时,先前的数据会无缝切换以显示新数据。
  • 提供isPreviousData来了解查询当前提供的数据。
function Todos() {
const [page, setPage] = React.useState(0)

const fetchProjects = (page = 0) => fetch('/api/projects?page=' + page).then((res) => res.json())

const {
isLoading,
isError,
error,
data,
isFetching,
isPreviousData,
} = useQuery({
queryKey: ['projects', page],
queryFn: () => fetchProjects(page),
keepPreviousData : true
})

return (
<div>
{isLoading ? (
<div>Loading...</div>
) : isError ? (
<div>Error: {error.message}</div>
) : (
<div>
{data.projects.map(project => (
<p key={project.id}>{project.name}</p>
))}
</div>
)}
<span>Current Page: {page + 1}</span>
<button
onClick={() => setPage(old => Math.max(old - 1, 0))}
disabled={page === 0}
>
Previous Page
</button>{' '}
<button
onClick={() => {
if (!isPreviousData && data.hasMore) {
setPage(old => old + 1)
}
}}
// Disable the Next Page button until we know a next page is available
disabled={isPreviousData || !data?.hasMore}
>
Next Page
</button>
{isFetching ? <span> Loading...</span> : null}{' '}
</div>
)
}

在无限查询结果中使用keepPreviousData

尽管不太常见,但keepPreviousData选项也可以与useInfiniteQuery hook完美配合使用,因此您可以在无限的查询键随时间变化时,无缝地让用户继续看到缓存的数据。